<!DOCTYPE html>
<html>
  <head>
    <title>cannon.js - constraints demo</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/style.css" type="text/css"/>
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  </head>
  <body>
    <script src="../build/cannon.js"></script>
    <script src="../build/cannon.demo.js"></script>
    <script src="../libs/dat.gui.js"></script>
    <script src="../libs/Three.js"></script>
    <script src="../libs/TrackballControls.js"></script>
    <script src="../libs/Detector.js"></script>
    <script src="../libs/Stats.js"></script>
    <script src="../libs/smoothie.js"></script>
    <script>

        /**
         * In this demo, we demonstrate some constraint types. Constraints
         * removes degrees of freedom from bodies and forces them to move in
         * a way defined by the constraint.
         */

        var demo = new CANNON.Demo();

        demo.addScene("lock",function(){
            var world = setupWorld(demo);
            world.solver.iterations = 20;
            world.gravity.set(0,0,-10);
            var size = 0.5;
            var boxShape = new CANNON.Box(new CANNON.Vec3(size,size,size));
            var mass = 1;
            var space = 0.1*size;

            var N=10, last;
            for(var i=0; i<N; i++){
                // Create a box
                var boxbody = new CANNON.Body({
                  mass: mass,
                  shape: boxShape,
                  position: new CANNON.Vec3((N-i-N/2)*(size*2+2*space), 0, size*6+space)
                });
                world.addBody(boxbody);
                demo.addVisual(boxbody);

                if(last){
                  // Connect the current body to the last one
                  var c = new CANNON.LockConstraint(boxbody, last);
                  world.addConstraint(c);
                }

                // To keep track of which body was added last
                last = boxbody;
            }

            // Create stands
            var bodyA = new CANNON.Body({
              mass: 0,
              shape: boxShape,
              position: new CANNON.Vec3((-N/2+1)*(size*2+2*space), 0, size*3)
            });
            world.addBody(bodyA);
            demo.addVisual(bodyA);

            var bodyB = new CANNON.Body({
              mass: 0,
              shape: boxShape,
              position: new CANNON.Vec3((N/2)*(size*2+2*space), 0, size*3)
            });
            world.addBody(bodyB);
            demo.addVisual(bodyB);
        });

        // We link together boxes in a chain
        demo.addScene("links",function(){
            var world = setupWorld(demo);
            world.gravity.set(0,-1,-20);
            var size = 1;
            var boxShape = new CANNON.Box(new CANNON.Vec3(size,size*0.1,size));
            var mass = 0;
            var space = 0.1*size;

            var N=10, last;
            for(var i=0; i<N; i++){
                // Create a box
                var boxbody = new CANNON.Body({ mass: mass });
                boxbody.addShape(boxShape);
                boxbody.position.set(0,0,(N-i)*(size*2+2*space) + size*2+space);
                boxbody.linearDamping=0.01; // Damping makes the movement slow down with time
                boxbody.angularDamping=0.01;
                world.addBody(boxbody);
                demo.addVisual(boxbody);

                if(i!=0){
                    // Connect the current body to the last one
                    // We connect two corner points to each other.
                    var c1 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(-size,0,size+space),last,new CANNON.Vec3(-size,0,-size-space));
                    var c2 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(size,0,size+space),last,new CANNON.Vec3(size,0,-size-space));
                    world.addConstraint(c1);
                    world.addConstraint(c2);
                } else {
                    // First body is now static. The rest should be dynamic.
                    mass=0.3;
                }

                // To keep track of which body was added last
                last = boxbody;
            }
        });

      // Particle cloth on sphere
      demo.addScene("cloth on sphere",function(){
          var world = setupWorld(demo);
          world.solver.iterations = 10;

          // To construct the cloth we need Nrows*Ncols particles.
          var dist = 0.2;
          var mass = 0.5;
          var Nrows = 15, Ncols = 15;
          var bodies = {}; // bodies["i j"] => particle
          for(var i=0; i<Ncols; i++){
            for(var j=0; j<Nrows; j++){
              // Create a new body
              var body = new CANNON.Body({ mass: mass });
              body.addShape(new CANNON.Particle());
              body.position.set((i-Ncols*0.5)*dist,(j-Nrows*0.5)*dist,5);
              bodies[i+" "+j] = body;
              world.addBody(body);
              demo.addVisual(body);
            }
          }
          // To connect two particles, we use a distance constraint. This forces the particles to be at a constant distance from each other.
          function connect(i1,j1,i2,j2){
              world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1],bodies[i2+" "+j2],dist));
          }
          for(var i=0; i<Ncols; i++){
            for(var j=0; j<Nrows; j++){
                // Connect particle at position (i,j) to (i+1,j) and to (i,j+1).
                if(i<Ncols-1) connect(i,j,i+1,j);
                if(j<Nrows-1) connect(i,j,i,j+1);
            }
          }

          // Add the static sphere we throw the cloth on top of
          var sphere = new CANNON.Sphere(1.5);
          var body = new CANNON.Body({ mass: 0 });
          body.addShape(sphere);
          body.position.set(0,0,3.5);
          world.addBody(body);
          demo.addVisual(body);
        });

        // A pendulum made out of two spheres using a PointToPointConstraint
        demo.addScene("Sphere pendulum",function(){
            var world = setupWorld(demo);
            var size = 1;
            var sphereShape = new CANNON.Sphere(size);
            var mass = 1;

            var spherebody = new CANNON.Body({ mass: mass });
            spherebody.addShape(sphereShape);
            spherebody.position.set(0,0,size*3);
            spherebody.velocity.set(5,0,0);
            spherebody.linearDamping=0;
            spherebody.angularDamping=0;
            world.addBody(spherebody);
            demo.addVisual(spherebody);

            var spherebody2 = new CANNON.Body({ mass: 0 });
            spherebody2.addShape(sphereShape);
            spherebody2.position.set(0,0,size*7);
            world.addBody(spherebody2);
            demo.addVisual(spherebody2);

            // Connect this body to the last one
            var c = new CANNON.PointToPointConstraint(spherebody,new CANNON.Vec3(0,0,size*2),spherebody2,new CANNON.Vec3(0,0,-size*2));
            world.addConstraint(c);
        });

      // Sphere chain
      demo.addScene("Sphere chain",function(){
          var size = 0.5;
          var dist = size*2+0.12;
          var world = setupWorld(demo);
          //world.solver.setSpookParams(1e20,3);
          var sphereShape = new CANNON.Sphere(size);
          var mass = 1;
          var lastBody = null;
          var N = 20;
          world.solver.iterations = N; // To be able to propagate force throw the chain of N spheres, we need at least N solver iterations.
          for(var i=0; i<N; i++){
            // Create a new body
            var spherebody = new CANNON.Body({ mass: i===0 ? 0 : mass });
            spherebody.addShape(sphereShape);
            spherebody.position.set(0,0,(N-i)*dist);
            spherebody.velocity.x = i;
            world.addBody(spherebody);
            demo.addVisual(spherebody);

            // Connect this body to the last one added
            var c;
            if(lastBody!==null){
              world.addConstraint(c = new CANNON.DistanceConstraint(spherebody,lastBody,dist));
            }

            // Keep track of the lastly added body
            lastBody = spherebody;
          }
        });



      // Particle cloth. Same as the previous cloth but here we make the first row of particles static, nailing the cloth it in space
      demo.addScene("Particle cloth",function(){
          var world = setupWorld(demo);
          //world.solver.setSpookParams(1e20,3);
          world.solver.iterations = 18;
          var dist = 0.2;
          var mass = 0.5;
          var Nrows = 15, Ncols = 15;
          var bodies = {}; // bodies["i j"] => particle
          for(var i=0; i<Ncols; i++){
            for(var j=0; j<Nrows; j++){
              // Create a new body
              var body = new CANNON.Body({ mass: j==Nrows-1 ? 0 : mass });
              body.addShape(new CANNON.Particle());
              body.position.set(i*dist,0,j*dist+5);
              body.velocity.set(0, 3*(Math.sin(i*0.1)+Math.sin(j*0.1)),0);
              bodies[i+" "+j] = body;
              world.addBody(body);
              demo.addVisual(body);
            }
          }
          function connect(i1,j1,i2,j2){
              world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1],bodies[i2+" "+j2],dist));
          }
          for(var i=0; i<Ncols; i++){
            for(var j=0; j<Nrows; j++){
              if(i<Ncols-1) connect(i,j,i+1,j);
              if(j<Nrows-1) connect(i,j,i,j+1);
            }
          }
        });

      // Particle 3d object
      // Distance constraints can be used to construct even cooler things, like this 3d block.
      demo.addScene("3D cloth structure",function(){
          var world = setupWorld(demo);
          world.solver.iterations = 10;
          var dist = 1;
          var mass = 1;
          var Nx = 6, Ny = 3, Nz = 3;
          var bodies = {}; // bodies["i j k"] => particle
          for(var i=0; i<Nx; i++){
            for(var j=0; j<Ny; j++){
              for(var k=0; k<Nz; k++){
                // Create a new body
                var body = new CANNON.Body({ mass: mass });
                body.addShape(new CANNON.Particle());
                body.position.set(i*dist,j*dist, k*dist + Nz*dist*0.3+1);
                body.velocity.set(0, 30*(Math.sin(i*0.1)+Math.sin(j*0.1)),0);
                bodies[i+" "+j+" "+k] = body;
                world.addBody(body);
                demo.addVisual(body);
              }
            }
          }
          function connect(i1,j1,k1,i2,j2,k2,len){
              world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1+" "+k1],bodies[i2+" "+j2+" "+k2],len));
          }
          for(var i=0; i<Nx; i++){
            for(var j=0; j<Ny; j++){
              for(var k=0; k<Nz; k++){
                // normal directions
                if(i<Nx-1) connect(i,j,k,  i+1,j,k, dist);
                if(j<Ny-1) connect(i,j,k,  i,j+1,k, dist);
                if(k<Nz-1) connect(i,j,k,  i,j,k+1, dist);

                // Diagonals
                if(i<Nx-1 && j<Ny-1 && k<Nz-1){
                  // 3d diagonals
                  connect(i,j,k,  i+1,j+1,k+1, Math.sqrt(3)*dist);
                  connect(i+1,j,k,  i,j+1,k+1, Math.sqrt(3)*dist);
                  connect(i,j+1,k,  i+1,j,k+1, Math.sqrt(3)*dist);
                  connect(i,j,k+1,  i+1,j+1,k, Math.sqrt(3)*dist);

                }

                // 2d diagonals
                if(i<Nx-1 && j<Ny-1){
                  connect(i+1,j,k,   i,j+1,k, Math.sqrt(2)*dist);
                  connect(i,j+1,k,   i+1,j,k, Math.sqrt(2)*dist);
                }
                if(i<Nx-1 && k<Nz-1){
                  connect(i+1,j,k,   i,j,k+1, Math.sqrt(2)*dist);
                  connect(i,j,k+1,   i+1,j,k, Math.sqrt(2)*dist);
                }
                if(j<Ny-1 && k<Nz-1){
                  connect(i,j+1,k,   i,j,k+1, Math.sqrt(2)*dist);
                  connect(i,j,k+1,   i,j+1,k, Math.sqrt(2)*dist);
                }
              }
            }
          }
        });


      function setupWorld(demo){
        // Create world
        var world = demo.getWorld();
        world.gravity.set(0,0,-40);
        world.broadphase = new CANNON.NaiveBroadphase();
        world.solver.iterations = 10;

        // ground plane
        var groundShape = new CANNON.Plane();
        var groundBody = new CANNON.Body({ mass: 0 });
        groundBody.addShape(groundShape);
        groundBody.position.set(0,0,1);
        world.addBody(groundBody);
        demo.addVisual(groundBody);

        world.quatNormalizeFast = false;
        world.quatNormalizeSkip = 0;

        return world;
      };

      demo.start();

    </script>
  </body>
</html>
